package cn.base.web.cahe;

import cn.hutool.core.util.ReflectUtil;
import cn.rengy.lang.CacheService;
import cn.rengy.util.ExceptionPrintUtils;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.Expiry;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.index.qual.NonNegative;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.Cache;
import org.springframework.cache.Cache.ValueRetrievalException;
import org.springframework.cache.Cache.ValueWrapper;
import org.springframework.cache.CacheManager;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import java.time.Duration;
import java.util.Collection;
import java.util.concurrent.Callable;

@Slf4j
@Component
public class DefaultCacheService implements CacheService, ApplicationContextAware {

	@Autowired(required = false)
	private CacheManager cacheManager;

	@Resource(name="caffeineCacheManager")
	private CacheManager caffeineCacheManager;

	@Value("${cache.type:'redis'}")
	private String cacheType;




	public CacheManager cacheManager() {
		CaffeineCacheManager cacheManager = new CaffeineCacheManager();

		Caffeine.newBuilder()
					.expireAfter(new Expiry<String, Object>() {
						@Override
						public long expireAfterCreate(@NonNull String key, @NonNull Object value, long currentTime) {
							return currentTime;
						}

						@Override
						public long expireAfterUpdate(@NonNull String key, @NonNull Object value, long currentTime, @NonNegative long currentDuration) {
							return currentDuration;
						}

						@Override
						public long expireAfterRead(@NonNull String key, @NonNull Object value, long currentTime, @NonNegative long currentDuration) {
							return currentDuration;
						}
					}).build();
					//.expireAfterWrite(Duration.ofSeconds(30))
					;
			//cacheManager.setCaffeine(caffeine);


			return cacheManager;
		}


	public <T> T getCache(String cacheName,Object key){
		if(key==null) {
			return null;
		}
		Cache cache=cacheManager.getCache(cacheName);
		ValueWrapper valueWrapper=cache.get(key.toString());
		if (valueWrapper != null) {
			return (T) valueWrapper.get();
		}
		return null;
	}
	@Override
	public <T> T getCache(String cacheName,String key,Callable<T> valueLoader) throws Exception{
		if(cacheType.equals("local")){
			return this.getLocalCache(cacheName,key,valueLoader);
		}
		//return cacheManager.getCache(cacheName).get(key, valueLoader);
		Cache cache=cacheManager.getCache(cacheName);
		ValueWrapper valueWrapper=cache.get(key.toString());
		if (valueWrapper != null) {
			return (T) valueWrapper.get();
		}
		T value;
//		try {
			value = valueLoader.call();
			if(value!=null&&value instanceof Collection) {
				if(((Collection)value).isEmpty()) {
					value=null;
				}
			}
//		} catch (Exception ex) {
//			log.error(ExceptionPrintUtils.getTrace(ex));
//			throw new ValueRetrievalException(key, valueLoader, ex);
//		}
		if(value!=null) {
			cache.put(key, value);
		}
		return value;
		
	}
	@Override
	public <T> T getCacheIncludeNull(String cacheName,String key,Callable<T> valueLoader) throws Exception{
		if(cacheType.equals("local")){
			return this.getLocalCache(cacheName,key.toString(),valueLoader);
		}
		//return cacheManager.getCache(cacheName).get(key, valueLoader);
		Cache cache=cacheManager.getCache(cacheName);
		ValueWrapper valueWrapper=cache.get(key.toString());
		if (valueWrapper != null) {
			return (T) valueWrapper.get();
		}
		T value;
//		try {
			value = valueLoader.call();
			if(value!=null&&value instanceof Collection) {
				if(((Collection)value).isEmpty()) {
					value=null;
				}
			}
//		} catch (Exception ex) {
//			log.error(ExceptionPrintUtils.getTrace(ex));
//			throw new ValueRetrievalException(key, valueLoader, ex);
//		}
		cache.put(key, value);
		return value;

	}

	@Override
	public <V> void set(String key, V value, Duration timeout) {

	}


	@Override
	public CacheManager getLocalCacheManager() {
		return caffeineCacheManager;
	}

	@Override
	public CacheManager getRedisCacheManager() {
		return cacheManager;
	}


	public <T> T getLocalCache(String cacheName, String key, Callable<T> valueLoader) throws Exception{
		//return localCacheManager.getCacheManager().getCache(cacheName).get(key, valueLoader);
		Cache cache= caffeineCacheManager.getCache(cacheName);
		ValueWrapper valueWrapper=cache.get(key);
		if (valueWrapper != null) {
			return (T) valueWrapper.get();
		}
		T value;
//		try {
			value = valueLoader.call();
			if(value!=null&&value instanceof Collection) {
				if(((Collection)value).isEmpty()) {
					value=null;
				}
			}
//		}catch (Exception ex) {
//			log.error(ExceptionPrintUtils.getTrace(ex));
//			//throw new ValueRetrievalException(key, valueLoader, ex);
//		}
		if(value!=null) {
			cache.put(key, value);
		}
		return value;
	}
	/**
	 * 缓存空值
	 * @param <T>
	 * @param cacheName
	 * @param key
	 * @param valueLoader
	 * @return
	 */
	@Override
	public <T> T getLocalCacheIncludeNull(String cacheName,String key,Callable<T> valueLoader) throws Exception{
		//return localCacheManager.getCacheManager().getCache(cacheName).get(key, valueLoader);
		Cache cache= caffeineCacheManager.getCache(cacheName);
		ValueWrapper valueWrapper=cache.get(key.toString());
		if (valueWrapper != null) {
			return (T) valueWrapper.get();
		}
		T value;
//		try {
			value = valueLoader.call();
			if(value!=null&&value instanceof Collection) {
				if(((Collection)value).isEmpty()) {
					value=null;
				}
			}
//		}catch (Exception ex) {
//			log.error(ExceptionPrintUtils.getTrace(ex));
//			throw new ValueRetrievalException(key, valueLoader, ex);
//		}
		cache.put(key, value);
		return value;
	}
	
	public Collection<String> listLocalCacheNames() {
		Collection<String> coll= caffeineCacheManager.getCacheNames();
		return coll;
	}
	/**
	 * 移除缓存
	 */
	public void evictLocalCache(String cacheName,String key) {
		
		Cache cache= caffeineCacheManager.getCache(cacheName);
		cache.evict(key);
	}
	/**
	 * 删除指定key缓存
	 */
	public void evict(String cacheName,String key){
		if(cacheManager!=null){
			Cache cache=cacheManager.getCache(cacheName);
			cache.evict(key);
		}
	}
	/**
	 * 清空指定缓存
	 */
	public void clear(String cacheName){
		Cache cache=cacheManager.getCache(cacheName);
		cache.clear();
	}

	private static ApplicationContext applicationContext;

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		this.applicationContext = applicationContext;
	}

	private <T> T getBean(String beanName) {
		return (T) applicationContext.getBean(beanName);
	}
}
